package com.xdxc.service.impl;

import io.lettuce.core.RedisException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Service
public class RedisService {
    private static final Logger logger = LoggerFactory.getLogger(RedisService.class);
    private final ConcurrentHashMap<String, ScheduledExecutorService> renewExecutors = new ConcurrentHashMap<>();
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 基础操作增强

    /**
     * 写入数据（带过期时间）
     *
     * @param expireTime 过期时间（秒）
     * @return
     */
    public boolean set(String key, Object value, long expireTime) {
        try {
            redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.MINUTES);
        } catch (RedisException e) {
            throw new RuntimeException("Redis操作失败");
        }
        return true;
    }

    /**
     * 读取数据（带泛型转换）
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key) {
        try {
            return (T) redisTemplate.opsForValue().get(key);
        } catch (RedisException e) {
            logger.error("Redis读取失败 Key: {}", key, e);
            return null;
        }
    }

    /**
     * 安全删除（返回是否删除成功）
     */
    public Boolean delete(String key) {
        try {
            return redisTemplate.delete(key);
        } catch (RedisException e) {
            logger.error("Redis删除失败 Key: {}", key, e);
            return false;
        }
    }

    // Hash操作扩展

    /**
     * Hash字段递增
     */
    public Long hashIncr(String key, String field, long delta) {
        return redisTemplate.opsForHash().increment(key, field, delta);
    }

    /**
     * 获取Hash所有字段
     */
    public Map<Object, Object> hashGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    // List操作

    public void listLeftPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    public Object listRightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    // Set操作
    public void setAdd(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }

    public Set<Object> setMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    // 分布式锁实现

    /**
     * 尝试获取分布式锁
     * @param waitTime 最大等待时间（秒）
     * @param leaseTime 锁持有时间（秒）
     */
    public boolean tryLock(String lockKey, String clientId, long waitTime, long leaseTime) {
        try {
            return Boolean.TRUE.equals(redisTemplate.execute(new RedisCallback<Boolean>() {
                @Override
                public Boolean doInRedis(RedisConnection connection) {
                    long begin = System.currentTimeMillis();
                    while (true) {
                        Boolean result = connection.set(
                                lockKey.getBytes(),
                                clientId.getBytes(),
                                Expiration.seconds(leaseTime),
                                RedisStringCommands.SetOption.SET_IF_ABSENT
                        );
                        if (result){
                            return true;
                        }
                        if (System.currentTimeMillis() - begin > waitTime * 1000) {
                            return false;
                        }
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return false;
                        }
                    }
                }
            }));
        } catch (Exception e) {
            logger.error("获取锁异常 Key: {}", lockKey, e);
            return false;
        }
    }


    private void startRenewalTask(String lockKey, String clientId, long leaseTime) {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        long interval = leaseTime * 1000 / 3; // 默认按过期时间的1/3间隔续期

        executor.scheduleAtFixedRate(() -> {
            if (!renewLock(lockKey, clientId, leaseTime)) {
                logger.warn("锁续期失败，即将释放资源 Key: {}", lockKey);
                executor.shutdown();
            }
        }, interval, interval, TimeUnit.MILLISECONDS);

        renewExecutors.put(lockKey, executor);
    }

    /**
     * 安全释放分布式锁
     * @param lockKey 锁键
     * @param clientId 客户端唯一标识（必须与加锁时使用的ID一致）
     */
    public boolean unlock(String lockKey, String clientId) {
        String luaScript =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "   return redis.call('del', KEYS[1]) " +
                        "else " +
                        "   return 0 " +
                        "end";

        try {
            Long result = redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
                    Collections.singletonList(lockKey),
                    clientId);
            return result != null && result == 1;
        } catch (Exception e) {
            logger.error("释放锁异常 Key: {}", lockKey, e);
            return false;
        }finally {
            // 停止续期线程
            stopRenewalTask(lockKey);
        }
    }
    private void stopRenewalTask(String lockKey) {
        ScheduledExecutorService executor = renewExecutors.remove(lockKey);
        if (executor != null && !executor.isShutdown()) {
            executor.shutdownNow();
        }
    }
    public boolean renewLock(String lockKey, String clientId, long expireTime) {
        String luaScript =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "   return redis.call('expire', KEYS[1], ARGV[2]) " +
                        "else " +
                        "   return 0 " +
                        "end";

        return Long.valueOf(1).equals(redisTemplate.execute(
                new DefaultRedisScript<>(luaScript, Long.class),
                Collections.singletonList(lockKey),
                clientId,
                String.valueOf(expireTime)
        ));
    }
    // 批量操作优化

    public void multiSet(Map<String, Object> map) {
        redisTemplate.opsForValue().multiSet(map);
    }

    public List<Object> multiGet(List<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }


    public void putAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 更新 Redis Hash 中的某个字段
     */
    public void updateField(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 获取 Redis Hash 中的所有字段和值
     */
    public Map<Object, Object> getAllFields(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取 Redis Hash 中的某个字段值
     */
    public Object getField(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 删除 Redis Hash 中的某个字段
     */
    public void deleteField(String key, String field) {
        redisTemplate.opsForHash().delete(key, field);
    }
    // 高级功能

    /**
     * 发布订阅消息
     */
    public void convertAndSend(String channel, Object message) {
        redisTemplate.convertAndSend(channel, message);
    }

    /**
     * 使用管道批量操作
     */
    public List<Object> pipelineExecute(RedisCallback<?> action) {
        return redisTemplate.executePipelined(action);
    }

    // 工具方法

    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    // 使用示例：
    // 1. 设置带过期时间的缓存：redisService.set("user:1001", user, 3600);
    // 2. 获取对象：User user = redisService.get("user:1001");
    // 3. 使用分布式锁：if(redisService.tryLock("order_lock", "client1", 5, 30)) {...}

    // 注意事项：
    // 1. 合理设置序列化器（建议使用Jackson2JsonRedisSerializer）
    // 2. 对于大value存储需要谨慎（建议不超过100KB）
    // 3. 重要操作添加事务支持
    // 4. 生产环境建议配置连接池参数
}